package org.apache.geronimo.daytrader.javaee6.web;

import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
import java.lang.management.*;
import org.apache.geronimo.daytrader.javaee6.utils.TradeConfig;

/**
 * 
 */
/**
 * @author rajat
 * This class is a singleton logger
 */
public class SingletonLogger {

    private static SingletonLogger instance;
    private static int SAMPLE_TIMER = 30; //IN Seconds
    private Vector<String> LogHistory;
    private static int arrivalcounter;
    private static int departcounter;
    private static int queuecounter;
    private Timer timer;
    private Object lock1 = new Object(); // used to lock
    private Object lock2 = new Object(); // used to lock
    private static ThreadMXBean threadbean = ManagementFactory.getThreadMXBean();
    private static MemoryMXBean memorybean = ManagementFactory.getMemoryMXBean();
    //static Vector<String> vc = new Vector<String>();   //creat vector vc for memory leak
    
    //Added by A. Erradi
    private static Vector leakingVector = null;  //used to simulate the leak
    //End Added by A. Erradi

    private SingletonLogger() {

        this.timer = new Timer();
        this.LogHistory = new Vector<String>();
        timer.scheduleAtFixedRate(new TimerTask() {

            public void run() {
                //do something here
                SingletonLogger.getInstance().dumpLogs();
            }
        }, SAMPLE_TIMER * 1000, SAMPLE_TIMER * 1000); // Will start with a delay of 30 seconds

    }

    public static synchronized SingletonLogger getInstance() {
        if (instance == null) {
            instance = new SingletonLogger();
        }
        return instance;
    }

    public void dumpLogs() {
        // synchronized(lock1)
//		{

        System.out.println("***** Time's up! *****");
        FileOutputStream raf = null;
        try {
            raf = new FileOutputStream("serverResponseTime.log", true);
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return;
        }
        FileChannel channel = raf.getChannel();
        FileLock lock = null;
        PrintStream p = null;

        // heap File Code Start
        FileOutputStream memraf = null;
        try {
            memraf = new FileOutputStream("CurrentHeapMemory.log", true);
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return;
        }
        FileChannel memchannel = memraf.getChannel();
        FileLock memlock = null;
        PrintStream pMem = null;
        // Heap File Code End
        try {
            lock = channel.lock();
            System.out.println("LogHistory Size is: " + LogHistory.size());
            //  PrintStream p = new PrintStream(new BufferedOutputStream(raf));
            p = new PrintStream(new BufferedOutputStream(raf));

            long currTime = System.currentTimeMillis();

            //@Abhishek:Thread usage

            int currthreadCount = threadbean.getThreadCount();

            int peakThreadCount = threadbean.getPeakThreadCount();

            int daemonThreads = threadbean.getDaemonThreadCount();



            //@Abhishek:reset peak count

            threadbean.resetPeakThreadCount();



            //@Abhishek:MemoryUsage
            String HeapMemory = memorybean.getHeapMemoryUsage().toString();
            long UsedHeapMemory = memorybean.getHeapMemoryUsage().getUsed()/1000000; 
            
            //         String NonHeapMemory=memorybean.getNonHeapMemoryUsage().toString();
            // Dump Heap Memroy Start
            try {
                memlock = memchannel.lock();
                System.out.println("LogHistory Size is: " + LogHistory.size());
                pMem = new PrintStream(new BufferedOutputStream(memraf));
                synchronized (lock2) {
                    //pMem.println(HeapMemory);
                    pMem.println(UsedHeapMemory);
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                return;
            } finally {
                if (memlock != null) {
                    try {
                        memlock.release();
                        pMem.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
            // Dump Heap Memroy Stop

            StringBuffer newEntry = new StringBuffer();

            // newEntry.append(currTime+":samplednumSrvReqCount=:"+queuecounter+":sampledarrivalCount=:"+arrivalcounter+":sampleddepartCount=:"+departcounter

            //                         +":PeakThreads=:"+peakThreadCount+":currentThreads=:"+currthreadCount+":daemonThreads=:"+daemonThreads+":HeapMemory=:"+HeapMemory);

            //  p.println(newEntry);

            System.out.println(currTime + ":samplednumSrvReqCount=:" + queuecounter + ":sampledarrivalCount=:" + arrivalcounter + ":sampleddepartCount=:" + departcounter
                    + ":PeakThreads=:" + peakThreadCount + ":currentThreads=:" + currthreadCount + ":daemonThreads=:" + daemonThreads + ":HeapMemory=:" + HeapMemory);


            //Added by A. Erradi
            int memoryLeakKiloBytes = TradeConfig.getMemoryLeakKiloBytes();
            if (memoryLeakKiloBytes > 0)
            {
                System.out.println("Added MemoryLeakKiloBytes : " + memoryLeakKiloBytes);

                if (leakingVector == null)
                    leakingVector = new Vector();
                leakingVector.add(new byte[memoryLeakKiloBytes * 1024]);
            }
            else
            {
                //Clear the content of the vector to release used memory
                if (leakingVector != null)
                {
                    System.out.println("Memory Leak Cleared. MemoryLeakKiloBytes : " + memoryLeakKiloBytes);
                    leakingVector.clear();
                    leakingVector = null;
                    System.gc(); //force the garbage collection
                }
            }
            //End Added by A. Erradi

            synchronized (lock1) {
                for (String Entry : LogHistory) {
                    p.println(Entry);

//Commented out by A. Erradi                  
//                    for (int i = 1; i < 10; i++) {
//                        vc.add("Vector Object i");
//                        //System.out.println("" + Runtime.getRuntime().freeMemory() + " bytes free!");
//                    }

                }

                LogHistory.clear();
                arrivalcounter = 0;
                departcounter = 0;


            }



            //    catch (FileNotFoundException e)
            //	{
            // TODO Auto-generated catch block
            //      e.printStackTrace();
//        return;
//      }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            return;
        } finally {
            if (lock != null) {
                try {
                    lock.release();
                    p.close();
                    //	 raf.close();

                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }

        }
        // }
    }

    public void insertLog(String message) {
        synchronized (lock1) {
            this.LogHistory.add(message);
        }
    }

    public int incrementCounter() {
        synchronized (lock1) {
            this.arrivalcounter += 1;
            this.queuecounter += 1;
            return this.arrivalcounter;

        }
    }

    public int decrementCounter() {
        synchronized (lock1) {
            this.departcounter += 1;
            this.queuecounter -= 1;
            return this.departcounter;
        }
    }

    public int getQueueCounter() {
        synchronized (lock1) {
            return this.queuecounter;
        }
    }
}
